<template>
  <!-- 外形：组件拖动 -->
  <div
    class="shape"
    :class="{ active: isActiveComponent(index) || isMultiSelected(index) }"
    @mousedown.stop="onMouseDown"
  >
    <slot></slot>
    <!-- 调整大小点阵 -->
    <div
      v-for="(item, i) in pointList"
      v-show="activeComponent.resize && isLayeredComponent(activeComponent) && filterResizePoint(item)"
      :key="item"
      class="shape-point"
      :style="getPointStyle(i, item)"
      @mousedown.stop.prevent="onShapeResize(item)"
    ></div>
    <div
      v-for="(num, i) in 4"
      :key="num"
      :class="`shape-line line-${['l', 't', 'r', 'b'][i]}`"
      :style="{ borderWidth: `${1 / scaleRatio}px` }"
    ></div>
  </div>
</template>

<script>
  import { mapGetters, mapMutations } from 'vuex';

  export default {
    name: 'Shape',
    props: {
      // 组件索引
      index: {
        type: Number,
        default: null
      },
      // 组件数据
      element: {
        type: Object,
        default: () => ({})
      },
      // style数据
      originStyle: {
        type: Object,
        default: () => ({})
      }
    },

    data() {
      // 八个伸缩的方向
      this.points = ['lt', 't', 'rt', 'r', 'rb', 'b', 'lb', 'l'];
      // 每个范围的角度对应的光标
      this.pointCursors = ['nw', 'n', 'ne', 'w', 'nw', 'n', 'ne', 'w'];
      return {};
    },

    inject: ['isH5'],

    computed: {
      ...mapGetters('component', [
        'componentData',
        'isActiveComponent',
        'isMultiSelected',
        'isLayeredComponent',
        'activeComponent',
        'multiSelectIndex',
        'scaleRatio',
        'previewer'
      ]),
      pointList() {
        if (!this.isActiveComponent(this.index)) return [];
        if (this.activeComponent.lock) {
          return ['lt', 'rt', 'rb', 'lb'];
        }
        return this.points;
      },
      pointCursorList() {
        if (!this.isActiveComponent(this.index)) return [];
        if (this.activeComponent.lock) {
          return ['nw', 'ne', 'nw', 'ne'];
        }
        return this.pointCursors;
      }
    },

    methods: {
      ...mapMutations('component', [
        'ACTIVE_COMPONENT',
        'HIDE_CONTEXTMENU',
        'SET_COMPONENTCLICK',
        'RECORD_SNAPSHOT',
        'MULTI_SELECT_COMPONENT'
      ]),

      // 拖动组件 多选组件
      onMouseDown(e) {
        // 关闭右键菜单列
        this.HIDE_CONTEXTMENU();
        // 点击是否为激活（此次点击是否保留组件激活）
        this.SET_COMPONENTCLICK(true);
        // 组件多选时
        if (e.ctrlKey && e.button === 0) {
          // 目前部分组件可被多选组合
          if (this.activeComponent && !this.activeComponent.isBasic) return;
          if (!this.componentData[this.index].isBasic) return;
          return this.MULTI_SELECT_COMPONENT(this.index);
        }
        // 多选下右键，不激活
        if (this.multiSelectIndex.length > 1 && this.isMultiSelected(this.index) && e.button === 2) return;
        // 组件拖动：
        this.ACTIVE_COMPONENT(this.index);
        if (this.element.style.position === 'relative') return;
        e.preventDefault();
        // 起始位置
        const startY = e.clientY;
        const startX = e.clientX;
        const startTop = this.originStyle.top * this.scaleRatio;
        const startLeft = this.originStyle.left * this.scaleRatio;

        // 用于判断此次点击为激活组件，非拖动组件
        let hasMove = false;
        const move = animationThrottle((moveEvent) => {
          hasMove = true;
          const curX = moveEvent.clientX;
          const curY = moveEvent.clientY;
          const endX = (curX - startX + startLeft) / this.scaleRatio;
          const endY = (curY - startY + startTop) / this.scaleRatio;
          const maxL = this.previewer.width - this.activeComponent.style.width;
          const maxT = this.previewer.height - this.activeComponent.style.height;
          // 画布边界处理
          // 上下边界
          if (endY <= 0) {
            this.originStyle.top = 0;
          } else if (this.isH5) {
            this.originStyle.top = endY;
            this.previewer.height = Math.max(this.activeComponent.style.height + endY, this.previewer.height);
          } else if (endY >= maxT) {
            this.originStyle.top = maxT;
          } else {
            this.originStyle.top = endY;
          }
          // 左右边界
          if (endX <= 0) {
            this.originStyle.left = 0;
          } else if (endX >= maxL) {
            this.originStyle.left = maxL;
          } else {
            this.originStyle.left = endX;
          }
          this.$nextTick(() => {
            this.$EventBus.$emit('hook:move', curY - startY > 0, curX - startX > 0);
          });
        });

        const up = () => {
          document.removeEventListener('mousemove', move);
          document.removeEventListener('mouseup', up);
          hasMove && this.RECORD_SNAPSHOT();
          this.$nextTick(() => {
            this.$EventBus.$emit('hook:unmove');
          });
        };

        document.addEventListener('mousemove', move);
        document.addEventListener('mouseup', up);
      },

      // 调整组件大小
      onShapeResize(point) {
        const downEvent = window.event;
        downEvent.stopPropagation();
        downEvent.preventDefault();

        const { height, width, top, left } = this.originStyle;
        const startX = downEvent.clientX;
        const startY = downEvent.clientY;

        const move = animationThrottle((moveEvent) => {
          const curX = moveEvent.clientX;
          const curY = moveEvent.clientY;
          const disY = (curY - startY) / this.scaleRatio;
          const disX = (curX - startX) / this.scaleRatio;
          const hasT = /t/.test(point);
          const hasB = /b/.test(point);
          const hasL = /l/.test(point);
          const hasR = /r/.test(point);
          let newHeight;
          let t;
          const newWidth = width + (hasL ? -disX : hasR ? disX : 0);
          const l = left + (hasL ? disX : 0);
          // 锁定快高比：根据宽度增加等比计算高度增加
          if (this.element.lock) {
            newHeight = (newWidth * height) / width;
            t = top + (hasT ? height - newHeight : 0);
            newHeight = Number(newHeight.toFixed(1));
            t = Number(t.toFixed(1));
          } else {
            newHeight = height + (hasT ? -disY : hasB ? disY : 0);
            t = top + (hasT ? disY : 0);
          }
          const h = newHeight > 0 ? newHeight : 0;
          const w = newWidth > 0 ? newWidth : 0;
          // 边界检测：允许从内向外拖，不允许从外向内拖
          if (t < 0 || h + t > this.previewer.height) return;
          if (l < 0 || w + l > this.previewer.width) return;
          h >= 1 && (this.originStyle.height = h);
          w >= 1 && (this.originStyle.width = w);
          w >= 1 && (this.originStyle.left = l);
          h >= 1 && (this.originStyle.top = t);
        });

        const up = () => {
          document.removeEventListener('mousemove', move);
          document.removeEventListener('mouseup', up);
        };

        document.addEventListener('mousemove', move);
        document.addEventListener('mouseup', up);
      },

      // 计算点阵位置
      getPointStyle(i, point) {

        const { width, height } = this.originStyle;
        const hasT = /t/.test(point);
        const hasB = /b/.test(point);
        const hasL = /l/.test(point);
        const hasR = /r/.test(point);
        let newLeft = 0;
        let newTop = 0;
        // 四个角的点
        if (point.length === 2) {
          newLeft = hasL ? 0 : width;
          newTop = hasT ? 0 : height;
        } else {
          // 上下两点的点，宽度居中
          if (hasT || hasB) {
            newLeft = width / 2;
            newTop = hasT ? 0 : height;
          }

          // 左右两边的点，高度居中
          if (hasL || hasR) {
            newLeft = hasL ? 0 : width;
            newTop = Math.floor(height / 2);
          }
        }
        const style = {
          marginLeft: '-4px',
          marginTop: '-4px',
          transform: `scale(${1 / this.scaleRatio})`,
          left: `${newLeft}px`,
          top: `${newTop}px`,
          cursor: `${this.pointCursorList[i]}-resize`
        };

        return style;
      },

      // 根据组件的resizePoints过滤显示
      filterResizePoint(point) {
        if (this.activeComponent.resizePoints) return this.activeComponent.resizePoints.includes(point);
        return true;
      }
    }
  };

  // 动画节流
  function animationThrottle(fun) {
    let animating = false;
    return function () {
      if (animating) return;
      animating = true;
      requestAnimationFrame(() => {
        fun(...arguments);
        animating = false;
      });
    };
  }
</script>

<style lang="scss" scoped>
  .shape {
    transform: translateZ(0);

    &:hover {
      cursor: move;
    }

    &.active > .shape-line {
      display: block;
    }

    // 点阵
    .shape-point {
      position: absolute;
      z-index: 999;
      border: 1px solid map-get($bdColors, 'theme');
      border-radius: 50%;
      width: 8px;
      height: 8px;
      background-color: #fff;
    }

    // 边线
    .shape-line {
      position: absolute;
      z-index: 998;
      display: none;
      // border-width: 1px;
      // border-style: dashed;
      // border-

      &.line-l,
      &.line-r {
        top: 0;
        width: 0;
        height: 100%;
      }

      &.line-t,
      &.line-b {
        left: 0;
        width: 100%;
        height: 0;
      }

      &.line-l {
        left: 0;
        border-left: 2px dashed map-get($bdColors, 'theme');
      }

      &.line-r {
        right: 0;
        border-right: 2px dashed map-get($bdColors, 'theme');
      }

      &.line-t {
        top: 0;
        border-top: 2px dashed map-get($bdColors, 'theme');
      }

      &.line-b {
        bottom: 0;
        border-bottom: 2px dashed map-get($bdColors, 'theme');
      }
    }
  }
</style>
